Skip to content

Fix/ssrf alerts target#1665

Open
ygndotgg wants to merge 4 commits into
parseablehq:mainfrom
ygndotgg:fix/ssrf-alerts-target
Open

Fix/ssrf alerts target#1665
ygndotgg wants to merge 4 commits into
parseablehq:mainfrom
ygndotgg:fix/ssrf-alerts-target

Conversation

@ygndotgg

@ygndotgg ygndotgg commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #XXXX.

Description

This PR hardens alert target delivery against server-side request forgery by adding an outbound policy for alert-target HTTP
requests.

Previously, authenticated users with alert target permissions could configure webhook, Slack, or AlertManager targets that
caused the Parseable server to send outbound HTTP requests to arbitrary URLs from the server’s network position. That meant
private/internal destinations, loopback services, link-local metadata endpoints, and other network-restricted HTTP services
could be reached if routable from the Parseable host.

The chosen fix is to centralize alert-target outbound networking behind a policy gate. Instead of validating only the submitted
URL string, Parseable now resolves the destination, validates the resolved IPs, applies admin-owned allow/deny policy, disables
redirects/proxies, pins the resolved DNS result into the HTTP client, and revalidates again immediately before alert delivery.

Key changes made in this patch:

  • Added alert target outbound policy configuration with:

    • allowPrivate
    • allowedDomains
    • allowedCidrs
    • deniedDomains
    • deniedCidrs
    • allowInvalidTls
  • Added super-admin APIs for reading, updating, and validating the alert target policy.

  • Applied outbound policy validation during target creation and update.

  • Revalidated outbound policy during alert delivery because stored targets and DNS answers can change after creation.

  • Blocked private, loopback, link-local, multicast, reserved, and other non-public destinations by default.

  • Allowed private/internal destinations only when explicitly enabled by admin policy and matched by an allowlist.

  • Made denied CIDRs/domains take precedence over allowlists.

  • Disabled redirects and proxy use for alert target delivery.

  • Pinned resolved DNS addresses into reqwest before sending.

  • Added focused unit tests for the important policy decision paths.


This PR has:

  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • been tested to ensure log ingestion and log query works.
  • added documentation for new or modified features or behaviors.

Summary by CodeRabbit

  • New Features

    • Added an outbound HTTP policy system for alert targets to control allowed/denied domains, CIDRs, TLS rules, and private-IP access.
    • Added admin HTTP endpoints to view, validate, and replace alert target policies.
    • Alert targets are pre-validated before saving and re-validated at delivery; certain authorization/headers may be blocked by policy.
  • Bug Fixes

    • Outbound policy rejections now return structured JSON error responses (400) with an admin hint.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dc930c95-163a-4fd3-b486-7976170ec664

📥 Commits

Reviewing files that changed from the base of the PR and between b8f9628 and a5327c8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • src/alerts/mod.rs
  • src/alerts/outbound_http_policy.rs
  • src/alerts/target.rs
  • src/handlers/http/alert_target_policy.rs
  • src/handlers/http/mod.rs
  • src/handlers/http/modal/query_server.rs
  • src/handlers/http/modal/server.rs
  • src/handlers/http/targets.rs
  • src/lib.rs
  • src/metastore/metastore_traits.rs
✅ Files skipped from review due to trivial changes (2)
  • src/lib.rs
  • src/metastore/metastore_traits.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/handlers/http/modal/query_server.rs
  • src/handlers/http/targets.rs
  • src/handlers/http/mod.rs
  • Cargo.toml
  • src/handlers/http/modal/server.rs
  • src/alerts/mod.rs
  • src/alerts/outbound_http_policy.rs
  • src/alerts/target.rs

Walkthrough

This PR adds outbound policy validation for alert targets, admin endpoints to read and update that policy, and delivery-time enforcement for Slack, webhooks, and AlertManager. It also validates outbound policy during target create and update operations.

Changes

Outbound Alert Target Policy System

Layer / File(s) Summary
Policy config and storage
Cargo.toml, src/alerts/outbound_http_policy.rs
tokio gains net; AlertTargetPolicyConfig and the active policy store add read, validate, and replace APIs with CIDR parsing.
Validation engine and request preparation
src/alerts/outbound_http_policy.rs
PreparedAlertTarget and OutboundPolicyError cover scheme, TLS, DNS, address, domain, and header checks; prepare_alert_target builds a pinned client and the module includes policy unit tests.
Error mapping and admin policy handlers
src/alerts/mod.rs, src/handlers/http/alert_target_policy.rs, src/handlers/http/mod.rs, src/handlers/http/modal/server.rs, src/handlers/http/modal/query_server.rs
AlertError adds an outbound-policy variant with JSON 400 handling, and the admin policy endpoints are exposed and routed for get, put, validate, and SuperAdmin access.
Target delivery and CRUD enforcement
src/alerts/target.rs, src/handlers/http/targets.rs
Target create/update paths now validate outbound policy, and alert delivery paths re-run preparation before sending Slack, webhook, and AlertManager requests.

Sequence Diagram(s)

sequenceDiagram
  participant AdminAPI
  participant PolicyHandlers
  participant validate_policy
  participant ALERT_TARGET_POLICY

  AdminAPI->>PolicyHandlers: PUT /admin/alert-target-policy
  PolicyHandlers->>validate_policy: validate policy
  validate_policy-->>PolicyHandlers: result
  alt valid
    PolicyHandlers->>ALERT_TARGET_POLICY: replace policy
    ALERT_TARGET_POLICY-->>PolicyHandlers: ok
    PolicyHandlers-->>AdminAPI: JSON policy
  else invalid
    PolicyHandlers-->>AdminAPI: error response
  end
Loading
sequenceDiagram
  participant TargetHandler
  participant prepare_alert_target
  participant DNSResolver
  participant reqwestClient
  participant AlertEndpoint

  TargetHandler->>prepare_alert_target: validate target
  prepare_alert_target->>DNSResolver: resolve host
  DNSResolver-->>prepare_alert_target: addresses
  prepare_alert_target-->>TargetHandler: prepared client or error
  alt allowed
    TargetHandler->>reqwestClient: send alert request
    reqwestClient->>AlertEndpoint: POST payload
    AlertEndpoint-->>reqwestClient: response
  else blocked
    TargetHandler->>TargetHandler: log and return
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • nikhilsinhaparseable

Poem

🐰 A hop for policies, neat and tight,
DNS checks left and right.
Alerts now pause before they fly,
Unless the rules say yes, not nigh.
Hop, hop—admins guard the gate!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and matches the main change: hardening alert targets against SSRF.
Description check ✅ Passed It follows the template with Fixes, a Description, key changes, and checklist items; only some checklist boxes remain unchecked.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
Cargo.toml (1)

84-91: 💤 Low value

ipnet addition not reflected in summary; otherwise dependency changes look correct.

The net feature on tokio ("^1.43") is valid and required for DNS resolution/socket operations in the policy engine. However, line 91 also adds ipnet = "2" (needed for CIDR parsing in the allow/deny policy), which contradicts the summary's claim that no other dependencies were altered.

Note that ipnet is placed under the "Async and Runtime" section though it's a networking/CIDR utility — consider moving it nearer url/networking deps for clarity, but this is purely cosmetic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Cargo.toml` around lines 84 - 91, Update the PR summary to explicitly mention
the new ipnet = "2" dependency (used for CIDR parsing in allow/deny policy) in
addition to the tokio feature change; in the Cargo.toml diff, ensure the ipnet
addition is clearly associated with networking/url deps rather than the "Async
and Runtime" block by moving the ipnet = "2" line next to other networking
dependencies (the tokio features block and url dependency) so reviewers can
easily see it's a networking/CIDR utility; keep the existing tokio feature
changes (sync, macros, fs, rt-multi-thread, net) unchanged.
src/alerts/outbound_http_policy.rs (1)

167-169: 💤 Low value

Consider propagating client build errors instead of panicking.

While the ClientBuilder configuration is deterministic and unlikely to fail in practice, using .expect() in production code paths is less defensive than propagating the error. If future changes introduce fallible configuration (e.g., loading certificates), this would need updating.

🛡️ Suggested fix

Add a new error variant and propagate:

+    #[error("failed to build HTTP client: {0}")]
+    ClientBuildFailed(String),
     let client = builder
         .build()
-        .expect("alert target HTTP client can be constructed");
+        .map_err(|e| OutboundPolicyError::ClientBuildFailed(e.to_string()))?;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/alerts/outbound_http_policy.rs` around lines 167 - 169, Replace the
panic-causing .expect("alert target HTTP client can be constructed") on
builder.build() with proper error propagation: add a new error variant (e.g.,
OutboundHttpClientBuildError or ClientBuildError) to the module's error enum,
change the containing function's return type to Result<..., ErrorType>, and
replace builder.build().expect(...) with builder.build().map_err(|e|
ErrorType::OutboundHttpClientBuildError(e.into()))? (or equivalent) so the
client construction failure is returned to callers; update any call sites and
tests to handle the propagated Result accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/alerts/outbound_http_policy.rs`:
- Around line 306-323: In denied_ipv6, add explicit checks to reject IPv6
tunneling prefixes 6to4 (2002::/16) and Teredo (2001::/32) before falling back
to mapped-IPv4 handling: inspect ip.segments()[0] and return true when it equals
0x2002 (6to4) or equals 0x2001 (Teredo /32), then continue with the existing
mapped-IPv4 call to denied_ipv4 and other checks; this ensures embedded IPv4
addresses in those tunneling ranges cannot bypass the private-IP blocking.

---

Nitpick comments:
In `@Cargo.toml`:
- Around line 84-91: Update the PR summary to explicitly mention the new ipnet =
"2" dependency (used for CIDR parsing in allow/deny policy) in addition to the
tokio feature change; in the Cargo.toml diff, ensure the ipnet addition is
clearly associated with networking/url deps rather than the "Async and Runtime"
block by moving the ipnet = "2" line next to other networking dependencies (the
tokio features block and url dependency) so reviewers can easily see it's a
networking/CIDR utility; keep the existing tokio feature changes (sync, macros,
fs, rt-multi-thread, net) unchanged.

In `@src/alerts/outbound_http_policy.rs`:
- Around line 167-169: Replace the panic-causing .expect("alert target HTTP
client can be constructed") on builder.build() with proper error propagation:
add a new error variant (e.g., OutboundHttpClientBuildError or ClientBuildError)
to the module's error enum, change the containing function's return type to
Result<..., ErrorType>, and replace builder.build().expect(...) with
builder.build().map_err(|e| ErrorType::OutboundHttpClientBuildError(e.into()))?
(or equivalent) so the client construction failure is returned to callers;
update any call sites and tests to handle the propagated Result accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 307e02c4-1433-45cc-90f4-a9ee10f8c67c

📥 Commits

Reviewing files that changed from the base of the PR and between cefe210 and fa45266.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • Cargo.toml
  • src/alerts/mod.rs
  • src/alerts/outbound_http_policy.rs
  • src/alerts/target.rs
  • src/handlers/http/alert_target_policy.rs
  • src/handlers/http/mod.rs
  • src/handlers/http/modal/query_server.rs
  • src/handlers/http/modal/server.rs
  • src/handlers/http/targets.rs

Comment thread src/alerts/outbound_http_policy.rs
@ygndotgg ygndotgg force-pushed the fix/ssrf-alerts-target branch from fa45266 to b8f9628 Compare June 2, 2026 13:08
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant